package com.iemp.filter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/* 验证过滤器 */
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final static Logger log = LoggerFactory.getLogger(JWTAuthenticationFilter.class);

    private final String salt;

    private final AuthenticationManager authenticationManager;

    private final Integer accessTokenExpirePeriod;
    private final Integer refreshTokenExpirePeriod;

    public JWTAuthenticationFilter(String salt, AuthenticationManager authenticationManager, Integer accessTokenExpirePeriod, Integer refreshTokenExpirePeriod) {
        this.salt = salt;
        this.authenticationManager = authenticationManager;
        this.accessTokenExpirePeriod = accessTokenExpirePeriod;
        this.refreshTokenExpirePeriod = refreshTokenExpirePeriod;
    }


    @Override
    /* 尝试验证 */
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {

        /* 从请求中读取 username, password */
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        log.info("Username is {}", username); log.info("Password is {}", password);

        /* 返回验证结果 */
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);
        return authenticationManager.authenticate(authenticationToken);
    }


    @Override
    /* 验证成功，分发 token */
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException {

        User user = (User) authentication.getPrincipal();
        Algorithm algorithm = Algorithm.HMAC256(salt.getBytes());

        /* build access_token */
        String access_token = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + accessTokenExpirePeriod * 60 * 1000))
                .withIssuer(request.getRequestURL().toString())
                .withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                .sign(algorithm);

        List<String> collect = user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
        log.info("authorization:{}", collect);

        /* build refresh_token */
        String refresh_token = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + refreshTokenExpirePeriod * 60 * 1000))
                .withIssuer(request.getRequestURL().toString())
                .sign(algorithm);

        /* cookie */
        Cookie cookie = new Cookie("access_token", access_token);
            cookie.setMaxAge(accessTokenExpirePeriod * 60);
        cookie.setPath("/"); // cookie 当前页面生效
        log.info("token: {}, val: {}, path:{}", access_token, cookie.getValue(), cookie.getPath());
        response.addCookie(cookie);

        /* 将 refresh_token 和 access_token 写入 response */
        Map<String, String> tokens = new HashMap<>();
        tokens.put("access_token", access_token);
        tokens.put("refresh_token", refresh_token);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getOutputStream(), tokens);




    }




}
